package com.zqh.apiordersubmit.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zqh.apiordersubmit.service.OrderSubmitService;
import com.zqh.myfmmall.clients.*;
import com.zqh.myfmmall.constants.OrderStatus;
import com.zqh.myfmmall.constants.PayType;
import com.zqh.myfmmall.result.Result;
import com.zqh.myfmmall.rowbeans.entity.*;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户 (Users)表服务实现类
 *
 * @author KONO 曾淇杭哒！
 * @since 2022-01-20 02:35:50
 */
@Slf4j
@Service("orderSubmitService")
public class OrderSubmitServiceImpl implements OrderSubmitService{
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ShoppingCartQueryClient shoppingCartQueryClient;
    @Autowired
    private UserAddrQueryClient userAddrQueryClient;
    @Autowired
    private OrderSaveClient orderSaveClient;
    @Autowired
    private ProductQueryClient productQueryClient;
    @Autowired
    private ProductSkuQueryClient productSkuQueryClient;
    @Autowired
    private OrderItemSaveClient orderItemSaveClient;
    @Autowired
    private ProductSkuCUDClient productSkuCUDClient;
    @Autowired
    private ShoppingCartCUDClient shoppingCartCUDClient;
    @Autowired
    private ProductImgQueryClient productImgQueryClient;
    @Autowired
    private WechatPayClient wechatPayClient;
    @Autowired
    private MqMessageClient mqMessageClient;



    @Override
    @GlobalTransactional
    public Result submitOrder(ArrayList<String> shoppingCartIds, String addrId, String userId){
        log.info("add order begin...");
        HashMap<String, String> orderinfoMap = null;
        //保存已经被加锁的套餐id及其对应的redisson锁，在最后用于释放已加锁的套餐
        HashMap<String, RLock> redissonLockMap = new HashMap<>();
        //验证库存
        Result shoppingCartResult = shoppingCartQueryClient.listByIds(shoppingCartIds);
        if (!shoppingCartResult.getSuccess()){
            return Result.internalServerError();
        }
        List<ShoppingCart> shoppingCarts = JSON.parseArray(JSON.toJSONString(shoppingCartResult.getData()), ShoppingCart.class);
        try {
            if (verifyStock(shoppingCarts)) {
                //对商品加分布式锁
                List<String> skuIds = shoppingCarts.stream().map(shoppingCart -> shoppingCart.getSkuId()).collect(Collectors.toList());
                boolean isLocked = redissonLock(skuIds,redissonLockMap);
                  //做测试时因为redis连接经常出错，可以暂时关闭reedisson
                //boolean isLocked = true;
                //整体加锁成功
                if (isLocked) {
                    //再次检验库存，以防在加锁期间库存发送改变，第一次验证库存是为了避免库存不足还进行redis加锁等操作浪费资源
                    if (verifyStock(shoppingCarts)) {
                        Result addrResult = userAddrQueryClient.getById(addrId);
                        if (!addrResult.getSuccess()){
                            return Result.internalServerError();
                        }
                        UserAddr addr = JSON.parseObject(JSON.toJSONString(addrResult.getData()),UserAddr.class);
                        Orders order = new Orders();
                        String orderId = UUID.randomUUID().toString();
                        //设置订单基本信息
                        order.setOrderId(orderId);
                        order.setUserId(userId);
                        order.setPayType(PayType.WECHATPAY);
                        order.setStatus(OrderStatus.NOT_PAYED);

                        //生成订单中的地址快照
                        order.setReceiverAddress(getAddressJsonString(addr));
                        order.setReceiverMobile(addr.getReceiverMobile());
                        order.setReceiverName(addr.getReceiverName());

                        //订单商品名称，用，分隔，发起支付微信等支付请求时作为商品描述参数
                        String productNames = "";
                        BigDecimal orderTotalAmount = new BigDecimal(0);

                        //获取订单中所有套餐用于生成套餐快照
                        //这里能这样查询集合的基础是，前端对同一套餐的加购行为进行识别，然后对原购物车项进行修改，而非添加新购物车项
                        Result productSkusResult = productSkuQueryClient.getByIds(skuIds);
                        if (!productSkusResult.getSuccess()){
                            return Result.internalServerError();
                        }
                        List<ProductSku> productSkus = JSON.parseArray(JSON.toJSONString(productSkusResult.getData()), ProductSku.class);

                        //获取订单中所有商品信息用于生成商品快照，由于若订单中有同一商品的不同套餐会导致集合中只有一个该商品后会集合越界
                        //现换为在for循环中一个一个获取商品
                        /*List<String> productIds = shoppingCarts.stream().map(s -> s.getProductId()).collect(Collectors.toList());
                        Result productsResult = productQueryClient.getByIds(productIds);
                        JavaType javaType3 = objectMapper.getTypeFactory().constructParametricType(List.class, Product.class);
                        List<Product> products = objectMapper.convertValue(productsResult.getData(),javaType3);*/

                        ArrayList<OrderItem> orderItems = new ArrayList<>();
                        //生成订单项
                        for (int i = 0;i < shoppingCarts.size(); i++) {
                            OrderItem orderItem = new OrderItem();
                            ProductSku productSku = productSkus.get(i);
                            Result productResult = productQueryClient.getById(shoppingCarts.get(i).getProductId());
                            if (!productResult.getSuccess()){
                                return Result.internalServerError();
                            }
                            Product product = JSON.parseObject(JSON.toJSONString(productResult.getData()), Product.class);
                            ShoppingCart shoppingCart = shoppingCarts.get(i);
                            productNames += product.getProductName() + ",";
                            orderItem.setOrderId(order.getOrderId());

                            //保存商品快照
                            orderItem.setProductName(product.getProductName());
                            orderItem.setProductId(product.getProductId());
                            Result mainImgResult = productImgQueryClient.getMainImg(product.getProductId());
                            if (!mainImgResult.getSuccess()){
                                return Result.internalServerError();
                            }
                            orderItem.setProductImg(((String) mainImgResult.getData()));

                            //保存套餐快照
                            orderItem.setSkuName(productSku.getSkuName());
                            orderItem.setProductPrice(productSku.getSellPrice());
                            orderItem.setSkuId(productSku.getSkuId());

                            orderItem.setBuyCounts(shoppingCart.getCartNum());
                            BigDecimal orderItemTotalAmount = new BigDecimal(String.valueOf(orderItem.getProductPrice()
                                    .multiply(new BigDecimal(String.valueOf(orderItem.getBuyCounts())))));
                            orderTotalAmount.add(orderItemTotalAmount);
                            orderItem.setTotalAmount(orderItemTotalAmount);
                            orderItem.setIsComment(0);
                            orderItems.add(orderItem);

                            //构造减少套餐库存后的套餐对象，这里新对象只赋值stock和skuId两个属性是为了更新服务中
                            //数据库操作的效率，其他未null的字段就不会进行更新
                            ProductSku skuAfterDeal = new ProductSku();
                            skuAfterDeal.setStock(productSku.getStock() - shoppingCart.getCartNum());
                            skuAfterDeal.setSkuId(productSku.getSkuId());
                            //替换集合中原来的套餐
                            productSkus.set(i,skuAfterDeal);
                        }

                        //提交套餐库存更新请求
                        Result updateSkuResult = productSkuCUDClient.updateBatch(productSkus);
                        if (!updateSkuResult.getSuccess()){
                            return Result.internalServerError();
                        }
                        //保存订单项
                        Result orderItemSaveResult = orderItemSaveClient.createBatch(orderItems);
                        if (!orderItemSaveResult.getSuccess()){
                            return Result.internalServerError();
                        }
                        //删除购物车
                        Result deleteShoppingCartResult = shoppingCartCUDClient.delete(shoppingCartIds);
                        if (!deleteShoppingCartResult.getSuccess()){
                            return Result.internalServerError();
                        }
                        //分布式事务及错误处理通过其他组件进行控制
                        /*boolean operationSuccess =
                                deleteShoppingCartResult.getStatusCode().equals(ResultType.SUCCESS.getCode()) &&
                                updateSkuResult.getStatusCode().equals(ResultType.SUCCESS.getCode()) &&
                                orderItemSaveResult.getStatusCode().equals(ResultType.SUCCESS.getCode());
                        if (!operationSuccess) {
                            RuntimeException runtimeException = new RuntimeException("提交订单过程中数据库更新异常");
                            log.info(""+runtimeException);
                            throw runtimeException;
                        }*/
                        order.setTotalAmount(orderTotalAmount);
                        //暂时没有加入优惠卷等功能故实际价格等于订单总价
                        order.setActualAmount(orderTotalAmount);
                        //将字符串最后的一个，去掉
                        productNames.substring(0, productNames.length());
                        order.setProductNames(productNames);
                        //保存订单
                        Result orderSaveResult = orderSaveClient.create(order);
                        if (!orderSaveResult.getSuccess()){
                            return Result.internalServerError();
                        }
                        orderinfoMap = new HashMap<>();
                        orderinfoMap.put("orderId", order.getOrderId());
                        orderinfoMap.put("productNames", order.getProductNames());
                        orderinfoMap.put("actualAmount", String.valueOf(order.getActualAmount()));
                        if (!wechatPay(orderinfoMap)) {
                            return Result.internalServerError();
                        }
                        if (!sendMqMessage(orderinfoMap.get("orderId"))) {
                            return Result.internalServerError();
                        }
                        log.info("add order success : " + orderinfoMap.get("orderId"));
                    }
                }
            }
        } catch (Exception e) {
            log.info("添加订单出现异常-" + e);
            return Result.internalServerError();
            //最后必定释放锁
        } finally {
            redissonUnlock(redissonLockMap);
        }
        return Result.success();
    }


    public boolean wechatPay(HashMap<String,String> orderInfoMap){
            Result wechatPayResult = wechatPayClient.pay(orderInfoMap);
            //发起微信支付失败
            return wechatPayResult.getSuccess();
    }


    public boolean sendMqMessage(String orderId){
        //发送mq死信,这里如果服务失败了要怎么处理，暂时不知道
        Result mqmessageResult = mqMessageClient.delayedMessage("order:" + orderId);
        return mqmessageResult.getSuccess();
    }


    public String getAddressJsonString(UserAddr addr) {
        HashMap<String, String> addrMap = new HashMap<>();
        ObjectMapper objectMapper = new ObjectMapper();

        addrMap.put("province", addr.getProvince());
        addrMap.put("city", addr.getCity());
        addrMap.put("area", addr.getArea());
        addrMap.put("addr", addr.getAddr());
        try {
            return objectMapper.writeValueAsString(addrMap);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean verifyStock(List<ShoppingCart> shoppingCarts) {
        List<String> skuIds = shoppingCarts.stream().map(s -> s.getSkuId()).collect(Collectors.toList());
        Result stockByIdsResult = productSkuQueryClient.getStockByIds(skuIds);
        List<ProductSku> productSkus = JSON.parseArray(JSON.toJSONString(stockByIdsResult.getData()), ProductSku.class);
        boolean enough = true;
        for (int i = 0; i < shoppingCarts.size(); i++) {
            enough = enough && productSkus.get(i).getStock() >= shoppingCarts.get(i).getCartNum();
        }
        return enough;
    }

    public boolean redissonLock(List<String> listToLock,HashMap<String, RLock> redissonLockMap) throws InterruptedException {
        boolean isLocked = true;
        for (String item : listToLock) {
            RLock lock = redissonClient.getLock(item);
            //添加该套餐的分布式锁（非阻塞锁），3秒重试时间，20s过期时间
            boolean lockSuccess = lock.tryLock(3, 20, TimeUnit.SECONDS);
            //只要有一个商品加锁失败那么整体就是加锁失败（这里只要有一次失败后islocked就会一直为false）
            isLocked = isLocked && lockSuccess;
            if (lockSuccess) {
                redissonLockMap.put(lock.getName(), lock);
            }
        }
        return isLocked;
    }



    public void redissonUnlock(HashMap<String, RLock> redissonLockMap){
        Collection<RLock> redissonLocks = redissonLockMap.values();
        for (RLock redissonLock : redissonLocks) {
            redissonLock.unlock();
        }
    }
}

